/**
OpenCV with OpenGL

OpenCVで取得した画像をOpenGLで表示する例

[2010/11/26] yosilove
**/

#include <cstdio>

using namespace std;

//OpenCVの
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv/cxcore.h>
IplImage* frame, *image, *gray, *image_bgra_ar, *image_bgr_gl;
CvMat* dft;
CvCapture* capture = 0;
int win_w, win_h;

//OpenGLの変数
#include <OpenGL/gl.h>     // The GL Header File
//#include <OpenGL/glu.h>
#include <GLUT/glut.h>   // The GL Utility Toolkit (Glut) Header

GLfloat   mat_ambient[]     = {0.0, 0.0, 1.0, 1.0};
GLfloat   mat_flash[]       = {0.0, 0.0, 0, 1.0};
GLfloat   mat_flash_shiny[] = {50.0};
GLfloat   light_position[]  = {100.0,-200.0,200.0,0.0};
GLfloat   ambi[]            = {0.1, 0.1, 0.1, 0.1};
GLfloat   lightZeroColor[]  = {0.1, 0.1, 0.1, 0.1};


float xrot = 0.0f;

void init(){
	printf("init()\n");

	capture = cvCaptureFromCAM(CV_CAP_ANY);
	frame = cvQueryFrame( capture );
	
	printf("- frame\n");
	
	if( frame ) {
		if( !image )
		{
			CvSize s = cvGetSize(frame);
			image = cvCreateImage(s , 8, 3 );
			image->origin = frame->origin;
			image_bgra_ar = cvCreateImage(s,8,4);
			image_bgra_ar->origin = frame->origin;
			
			gray = cvCreateImage(s,8,1);
			gray->origin = frame->origin;
			image_bgr_gl = cvCreateImage(s,8,3);
		}
		win_w = frame->width;
		win_h = frame->height;
	}
	
	printf("- set OpenGL\n");
//OpenGL --

	glShadeModel(GL_FLAT);							// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
	glClearDepth(1.0f);									// Depth Buffer Setup
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	
	gluLookAt(0.0, 0.0, 5.0,  /* eye is at (0,0,5) */
    0.0, 0.0, 0.0,      /* center is at (0,0,0) */
    0.0, 1.0, 0.);      /* up is in positive Y direction */
//-- OpenGL

	printf("init() --end \n");
}

void display ( void )   // Create The Display Function
{
//OpenCV

	int c;
	bool doMult = false;

	frame = cvQueryFrame( capture );
	if( frame ) {

		cvCopy( frame, image);
		cvCvtColor(image,gray,CV_RGB2GRAY);
		cvCvtColor(gray,image_bgra_ar,CV_GRAY2BGRA);	
		cvCvtColor(frame,image_bgr_gl,CV_RGB2BGR);
		cvFlip(image_bgr_gl);
	}

//OpenGL --
	// Clear Screen And Depth Buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	

	glDisable(GL_LIGHTING);

	//背景を描画
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(0.0,win_w, 0.0,win_h);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	//OpenCVで取得したIplImageを背景画像として設定
	glDrawPixels(win_w,win_h,GL_RGB,GL_UNSIGNED_BYTE,image_bgr_gl->imageData);

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();

	glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//照明の設定 --
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambi);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightZeroColor);
    //glMaterialfv(GL_FRONT, GL_SPECULAR, mat_flash);
    //glMaterialfv(GL_FRONT, GL_SHININESS, mat_flash_shiny);	
    //glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    //-- 照明の設定
    
    //オブジェクトの設定 --
    //とりあえず立方体でも出力しとく
    glMatrixMode(GL_MODELVIEW);
    glTranslatef(0.0, 0.0, -1.0);
    glScalef(.02f,.02f,.02f);
	glRotatef(xrot,0.0f,0.0f,1.0f);
	xrot+=5.0f;
	glRotated(90,1,0,0);
	//glTranslated(-55,0,0);
	glutSolidCube(50.0);
	glPopMatrix();
	//--オブジェクトの設定
	
    glDisable( GL_LIGHTING );
    glDisable( GL_DEPTH_TEST );
	glutSwapBuffers ( );
  // Swap The Buffers To Not Be Left With A Clear Screen
}

void reshape ( int w, int h )   // Create The Reshape Function (the viewport)
{
	printf("reshape() \n");
	glViewport     ( 0, 0, w, h );
	glMatrixMode   ( GL_PROJECTION );  // Select The Projection Matrix
	glLoadIdentity ( );                // Reset The Projection Matrix
	if ( h==0 )  // Calculate The Aspect Ratio Of The Window
    	gluPerspective ( 80, ( float ) w, 1.0, 5000.0 );
	else
 		gluPerspective ( 80, ( float ) w / ( float ) h, 1.0, 5000.0 );
	glMatrixMode   ( GL_MODELVIEW );  // Select The Model View Matrix
	glLoadIdentity ( );    // Reset The Model View Matrix
  
	printf("reshape() --end \n");
}

/**キー用のコールバック**/
void keyboard ( unsigned char key, int x, int y )  // Create Keyboard Function
{
  switch ( key ) {
    case 27:        // When Escape Is Pressed...
      exit ( 0 );   // Exit The Program
      break;        // Ready For Next Case
    default:        // Now Wrap It Up
      break;
  }
}

/**特殊キー用のコールバック**/
void arrow_keys ( int a_keys, int x, int y )  // Create Special Function (required for arrow keys)
{
  switch ( a_keys ) {
    case GLUT_KEY_UP:     // When Up Arrow Is Pressed...
      glutFullScreen ( ); // Go Into Full Screen Mode
      break;
    case GLUT_KEY_DOWN:               // When Down Arrow Is Pressed...
      glutReshapeWindow ( 500, 500 ); // Go Into A 500 By 500 Window
      break;
    default:
      break;
  }
}

int main(int argc,char* argv[])
{
	printf("main() \n");
	//コールバック設定 --
	glutInit( &argc, argv ); // Erm Just Write It =)
	glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); // Display Mode
	
	init();
	
	// If glutFullScreen wasn't called this is the window size
	glutInitWindowSize( win_w, win_h ); 
	// Window Title (argv[0] for current directory as title)
	glutCreateWindow( "OpenCV+OpenGL" ); 
//	glutFullScreen      ( );          // Put Into Full Screen
	glutDisplayFunc( display );  // Matching Earlier Functions To Their Counterparts
	glutIdleFunc(display);
	glutReshapeFunc( reshape );//
	glutKeyboardFunc ( keyboard );//キーボートのコールバック
	glutSpecialFunc( arrow_keys );//特殊キーのコールバック
	//-- コールバック設定
	
	printf("mainLoop() \n");
	glutMainLoop( );          // Initialize The Main Loop

	cvReleaseCapture(&capture);
	cvReleaseImage(&image);
	//cvReleaseImage(&frame);
	cvReleaseImage(&image_bgra_ar);
	cvDestroyAllWindows();

	return 0;
}